﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using AWS.SLAM.Configuration;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Text.RegularExpressions;
using AWS.SLAM.Logging;
using System.Diagnostics;
using System.Threading;

namespace AWS.SLAM.SharePoint
{
    public class SharePointServices
    {
        #region Private State
        
        private ILogger _logger;
        private int _sequenceNumber;

        #endregion

        #region Constructors

        public SharePointServices(ILogger logger)
        {
            _logger = logger;
            _sequenceNumber = 200;
        }

        #endregion

        public void AttachContentTypeEventHandler(string siteName, string contentTypeName, SPEventReceiverType eventReceiverType, string assemblyName, string className)
        {
            SPSite siteCollection = SPControl.GetContextSite(HttpContext.Current);

            SPWeb site = null;
            if (String.IsNullOrEmpty(siteName))
            {
                site = siteCollection.RootWeb;
            }
            else
            {
                try
                {
                    site = siteCollection.AllWebs[siteName];
                }
                catch { }
            }

            if (site != null && site.Exists)
            {
                site.AllowUnsafeUpdates = true;

                SPContentType contentType = null;

                try
                {
                    contentType = site.ContentTypes[contentTypeName];
                }
                catch
                {
                    //content type not found
                }

                if (contentType != null)
                {
                    //verify event reciever is not duplicate
                    bool exists = false;
                    foreach (SPEventReceiverDefinition reciever in contentType.EventReceivers)
                    {
                        if (reciever.Name == siteName + contentTypeName + eventReceiverType.ToString())
                        {
                            exists = true;
                            break;
                        }
                    }
                    if (!exists)
                    {
                        string receiverName = "SLAM" + siteName + contentTypeName + eventReceiverType.ToString();
                        SPEventReceiverDefinition eventReceiver = contentType.EventReceivers.Add();
                        eventReceiver.Name = receiverName;
                        eventReceiver.Assembly = assemblyName;
                        eventReceiver.Class = className;
                        eventReceiver.Type = eventReceiverType;
                        eventReceiver.SequenceNumber = _sequenceNumber;
                        eventReceiver.Data = contentTypeName;
                        eventReceiver.Update();
                        contentType.Update(true, false);

                        _sequenceNumber++;
                    }

                }

                site.Dispose();
            }
        }

        public void AttachListEventHandler(string siteName, string listId, string listName, SPEventReceiverType eventReceiverType, string assemblyName, string className)
        {
            SPSite siteCollection = SPControl.GetContextSite(HttpContext.Current);

            SPWeb site = null;
            if (String.IsNullOrEmpty(siteName) || siteName == "*")
            {
                site = siteCollection.RootWeb;
            }
            else
            {
                try
                {
                    site = siteCollection.AllWebs[siteName];
                }
                catch { }
            }

            SPList list = null;
            bool exists = false;

            if (site != null && site.Exists)
            {
                site.AllowUnsafeUpdates = true;

                try
                {
                    if (!String.IsNullOrEmpty(listId))
                    {
                        list = site.Lists[new Guid(listId)];
                    }
                    else
                    {
                        list = site.Lists[listName];
                    }
                }
                catch { }

                if (list != null)
                {
                    //verify event reciever is not duplicate
                    foreach (SPEventReceiverDefinition reciever in list.EventReceivers)
                    {
                        if (reciever.Name == siteName + listName + eventReceiverType.ToString())
                        {
                            exists = true;
                            break;
                        }
                    }

                    if (!exists)
                    {
                        SPEventReceiverDefinition eventReceiver = list.EventReceivers.Add();
                        eventReceiver.Name = "SLAM" + siteName + listName + eventReceiverType.ToString();
                        eventReceiver.Assembly = assemblyName;
                        eventReceiver.Class = className;
                        eventReceiver.Type = eventReceiverType;
                        eventReceiver.SequenceNumber = _sequenceNumber;
                        eventReceiver.Update();
                        list.Update();

                        _sequenceNumber++;
                    }
                }
                site.Dispose();
            }

            if (siteName == "*")
            {
                foreach (SPWeb nextWeb in site.Site.AllWebs)
                {
                    try
                    {
                        list = null;
                        list = nextWeb.Lists[listName];

                        //verify event reciever is not duplicate
                        exists = false;
                        foreach (SPEventReceiverDefinition reciever in list.EventReceivers)
                        {
                            if (reciever.Name == siteName + listName + eventReceiverType.ToString())
                            {
                                exists = true;
                                break;
                            }
                        }
                        if (!exists)
                        {
                            site.AllowUnsafeUpdates = true;

                            SPEventReceiverDefinition eventReceiver = list.EventReceivers.Add();
                            eventReceiver.Name = "SLAM" + siteName + listName + eventReceiverType.ToString();
                            eventReceiver.Assembly = assemblyName;
                            eventReceiver.Class = className;
                            eventReceiver.Type = eventReceiverType;
                            eventReceiver.SequenceNumber = _sequenceNumber;
                            eventReceiver.Update();
                            //list.EventReceivers.Add(eventReceiverType, assemblyName, className);
                            list.Update();

                            _sequenceNumber++;
                        }
                    }
                    catch { }
                    nextWeb.Dispose();
                }
            }
        }

        /// <summary>
        /// Creates a new ContentType with the given name and collection of fields.
        /// </summary>
        /// <param name="contentTypeName"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        public bool CreateContentType(string siteName, string contentTypeName, IEnumerable<Field> fields)
        {
            bool success = true;
            SPSite siteCollection = SPControl.GetContextSite(HttpContext.Current);

            SPWeb site = null;
            if (String.IsNullOrEmpty(siteName))
            {
                site = siteCollection.RootWeb;
            }
            else
            {
                site = siteCollection.AllWebs[siteName];
            }

            if (site != null && site.Exists)
            {
                site.AllowUnsafeUpdates = true;
                try
                {

                    SPContentType itemContentType = site.AvailableContentTypes["Item"];
                    SPContentType newContentType = new SPContentType(itemContentType, site.ContentTypes, contentTypeName);
                    newContentType.Group = "List Content Types";
                    site.ContentTypes.Add(newContentType);
                    site.Update();

                    foreach (Field newField in fields)
                    {
                        if (TypeDescriptor.GetProperties(newContentType).Find(newField.Name, true) == null)
                        {
                            SPField addField = null;
                            if (!site.Fields.ContainsField(newField.Name))
                            {
                                addField = site.Fields.CreateNewField(newField.SharePointType, newField.Name);
                                site.Fields.Add(addField);
                                site.Update();
                            }

                            SPFieldLink fieldLink = new SPFieldLink(site.Fields[newField.Name]);
                            //fieldLink.Required = newField.Required;
                            newContentType.FieldLinks.Add(fieldLink);
                            newContentType.Update();
                        }
                    }
                }
                catch
                {
                    success = false;
                }

                site.Dispose();
            }
            return success;
        }

        /// <summary>
        /// Creates a new List with the given name and collection of fields.
        /// </summary>
        /// <param name="listName"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        public bool CreateList(string siteName, string listName, IEnumerable fields)
        {
            throw new NotImplementedException();

            //bool success = true;

            //SPSecurity.RunWithElevatedPrivileges(delegate()
            //{
                //SPWeb site = null;
                //if (String.IsNullOrEmpty(siteName))
                //{
                //    site = siteCollection.RootWeb;
                //}
                //else
                //{
                //    site = siteCollection.AllWebs[siteName];
                //}

                //site.Dispose();
            //});
            //return success;
        }

        public void DetachAllEventHandlers(string assemblyName, string listClassName, string contentTypeClassName)
        {
            TraceLevel currentTraceLevel = TraceLevel.Error;

            //Check if the logger is the default SqlLogger
            SqlLogger sqlLogger = _logger is SqlLogger ? ((SqlLogger)_logger) : null;
            //If it is, force it to log Activation's messages regardless of the
            //configured trace level
            if (sqlLogger != null)
            {
                currentTraceLevel = sqlLogger.Switch.Level;
                sqlLogger.Switch.Level = TraceLevel.Verbose;
            }

            SPSite siteCollection = SPControl.GetContextSite(HttpContext.Current);

            foreach (SPWeb site in siteCollection.AllWebs)
            {
                site.AllowUnsafeUpdates = true;

                bool performLogMessage = false;
                
                //Detach all event handlers for content types within this site
                for (int c = 0; c < site.ContentTypes.Count; c++)
                {
                    //This is a little ridiculous, but because we are using
                    //contentType.Update(true, false) to propagate the event receiver deletion to
                    //the children of this content type without the "innerSite" here opened for every one
                    //this process will throw an exception with the message:
                    //"The object has been updated by another user since it was last fetched"
                    using (SPWeb innerSite = siteCollection.OpenWeb(site.ID))
                    {
                        SPContentType contentType = innerSite.ContentTypes[site.ContentTypes[c].Id];
                        if (!contentType.Sealed && !contentType.ReadOnly)
                        {
                            bool updateContentType = false;
                            for (int i = 0; i < contentType.EventReceivers.Count; i++)
                            {
                                //Check if the assembly name, at the beginning, and the PublicKeyToken, at the end
                                //of the full Assembly name matches.  This should detach event handlers
                                //from old versions.
                                if ((contentType.EventReceivers[i].Assembly.StartsWith(assemblyName.Substring(0, assemblyName.IndexOf(",")))) ||
                                    contentType.EventReceivers[i].Name.StartsWith("SLAM"))
                                {
                                    contentType.EventReceivers[i].Delete();
                                    i--;
                                    performLogMessage = true;
                                    updateContentType = true;
                                }
                            }

                            if (updateContentType)
                            {
                                contentType.Update(true, false);
                            }

                            if (performLogMessage)
                            {
                                string typeString = "";
                                if (String.IsNullOrEmpty(site.ServerRelativeUrl.TrimEnd('/')))
                                {
                                    typeString = String.Format("{0} - {1}", site.ServerRelativeUrl.TrimEnd('/'), contentType.Name);
                                }
                                else
                                {
                                    typeString = contentType.Name;
                                }
                                _logger.LogMessage("Information", "Detached Event Handlers for Content Type: " + typeString);
                            }

                            performLogMessage = false;
                        }
                    }
                }

                //Detach all event handlers for Lists within this site
                for (int l = 0; l < site.Lists.Count; l++)
                {
                    SPList list = site.Lists[l];
                    for (int i = 0; i < list.EventReceivers.Count; i++)
                    {
                        //Check if the assembly name, at the beginning, and the PublicKeyToken, at the end
                        //of the full Assembly name matches.  This should detach event handlers
                        //from old versions.
                        if ((list.EventReceivers[i].Assembly.StartsWith(assemblyName.Substring(0, assemblyName.IndexOf(",")))) ||
                            list.EventReceivers[i].Name.StartsWith("SLAM"))
                        {
                            list.EventReceivers[i].Delete();
                            i--;
                            performLogMessage = true;
                        }
                    }

                    list.Update();

                    if (performLogMessage)
                    {
                        string typeString = "";
                        if (String.IsNullOrEmpty(site.ServerRelativeUrl.TrimEnd('/')))
                        {
                            typeString = String.Format("{0} - {1} - {2}", site.ServerRelativeUrl.TrimEnd('/'), list.ID.ToString(), list.Title);
                        }
                        else
                        {
                            typeString = String.Format("{0} - {1}", list.ID.ToString(), list.Title);
                        }
                        _logger.LogMessage("Information", "Detached Event Handlers for List: " + typeString);
                    }

                    performLogMessage = false;
                }

                site.Dispose();
            }
        }

        public void DetachContentTypeEventHandlers(string siteName, string contentTypeName, string assemblyName, string className)
        {
            SPSite siteCollection = SPControl.GetContextSite(HttpContext.Current);

            SPWeb site = null;
            if (String.IsNullOrEmpty(siteName))
            {
                site = siteCollection.RootWeb;
            }
            else
            {
                site = siteCollection.AllWebs[siteName];
            }

            if (site != null && site.Exists)
            {
                site.AllowUnsafeUpdates = true;

                SPContentType contentType = site.ContentTypes[contentTypeName];

                bool updateContentType = false;
                for (int i = 0; i < contentType.EventReceivers.Count; i++)
                {
                    //Check if the assembly name, at the beginning, and the PublicKeyToken, at the end
                    //of the full Assembly name matches.  This should detach event handlers
                    //from old versions.
                    if ((contentType.EventReceivers[i].Assembly.StartsWith(assemblyName.Substring(0, assemblyName.IndexOf(",") + 1)) &&
                        contentType.EventReceivers[i].Assembly.EndsWith(assemblyName.Substring(assemblyName.LastIndexOf(",") + 1)) &&
                        contentType.EventReceivers[i].Class == className) ||
                        contentType.EventReceivers[i].Name.StartsWith("SLAM"))
                    {
                        contentType.EventReceivers[i].Delete();
                        i--;
                        updateContentType = true;
                    }
                }

                if (updateContentType)
                {
                    contentType.Update(true, false);
                }
                site.Dispose();
            }
        }

        public void DetachListContentTypeEventHandlers(string siteName, string listId, string listName, string assemblyName, string className)
        {
            SPSite siteCollection = SPControl.GetContextSite(HttpContext.Current);

            SPWeb site = null;
            if (String.IsNullOrEmpty(siteName))
            {
                site = siteCollection.RootWeb;
            }
            else
            {
                site = siteCollection.AllWebs[siteName];
            }

            if (site != null && site.Exists)
            {
                site.AllowUnsafeUpdates = true;

                SPList list = null;
                try
                {
                    if (!String.IsNullOrEmpty(listId))
                    {
                        list = site.Lists[new Guid(listId)];
                    }
                    else
                    {
                        list = site.Lists[listName];
                    }
                }
                catch { }

                if (list != null)
                {
                    foreach (SPContentType contentType in list.ContentTypes)
                    {
                        bool updateContentType = false;
                        for (int i = 0; i < contentType.EventReceivers.Count; i++)
                        {
                            //Check if the assembly name, at the beginning, and the PublicKeyToken, at the end
                            //of the full Assembly name matches.  This should detach event handlers
                            //from old versions.
                            if ((contentType.EventReceivers[i].Assembly.StartsWith(assemblyName.Substring(0, assemblyName.IndexOf(",") + 1)) &&
                                contentType.EventReceivers[i].Assembly.EndsWith(assemblyName.Substring(assemblyName.LastIndexOf(",") + 1)) &&
                                contentType.EventReceivers[i].Class == className) ||
                                contentType.EventReceivers[i].Name.StartsWith("SLAM"))
                            {
                                contentType.EventReceivers[i].Delete();
                                i--;
                                updateContentType = true;
                            }
                        }

                        if (updateContentType)
                        {
                            contentType.Update(true, false);
                        }
                    }

                    list.Update();
                }

                site.Dispose();
            }
        }

        public void DetachListEventHandlers(string siteName, string listId, string listName, string assemblyName, string className)
        {
            SPSite siteCollection = SPControl.GetContextSite(HttpContext.Current);

            SPWeb site = null;
            try
            {
                if (String.IsNullOrEmpty(siteName) || siteName == "*")
                {
                    site = siteCollection.RootWeb;
                }
                else
                {
                    site = siteCollection.AllWebs[siteName];
                }
            }
            catch { }

            if (site != null)
            {
                SPList list = null;

                if (site.Exists)
                {
                    site.AllowUnsafeUpdates = true;

                    try
                    {
                        if (!String.IsNullOrEmpty(listId))
                        {
                            list = site.Lists[new Guid(listId)];
                        }
                        else
                        {
                            list = site.Lists[listName];
                        }
                    }
                    catch { }
                    if (list != null)
                    {
                        for (int i = 0; i < list.EventReceivers.Count; i++)
                        {
                            //Check if the assembly name, at the beginning, and the PublicKeyToken, at the end
                            //of the full Assembly name matches.  This should detach event handlers
                            //from old versions.
                            if ((list.EventReceivers[i].Assembly.StartsWith(assemblyName.Substring(0, assemblyName.IndexOf(",") + 1)) &&
                                list.EventReceivers[i].Assembly.EndsWith(assemblyName.Substring(assemblyName.LastIndexOf(",") + 1)) &&
                                list.EventReceivers[i].Class == className) ||
                                list.EventReceivers[i].Name.StartsWith("SLAM"))
                            {
                                list.EventReceivers[i].Delete();
                                i--;
                            }
                        }

                        list.Update();
                    }



                    if (siteName == "*")
                    {
                        foreach (SPWeb nextWeb in site.Site.AllWebs)
                        {
                            try
                            {
                                list = null;
                                try
                                {
                                    if (!String.IsNullOrEmpty(listId))
                                    {
                                        list = nextWeb.Lists[new Guid(listId)];
                                    }
                                    else
                                    {
                                        list = nextWeb.Lists[listName];
                                    }
                                }
                                catch { }
                                if (list != null)
                                {
                                    for (int i = 0; i < list.EventReceivers.Count; i++)
                                    {
                                        if (list.EventReceivers[i].Assembly == assemblyName && list.EventReceivers[i].Class == className)
                                        {
                                            list.EventReceivers[i].Delete();
                                            i--;
                                        }
                                    }

                                    list.Update();
                                }
                            }
                            catch { }
                            nextWeb.Dispose();
                        }
                    }

                    site.Dispose();
                }
            }
        }

        public bool VerifyContentTypeExists(string siteName, string contentTypeName)
        {
            bool contentTypeExists = true;

            SPSite siteCollection = SPControl.GetContextSite(HttpContext.Current);

            SPWeb site = null;
            if (String.IsNullOrEmpty(siteName))
            {
                site = siteCollection.RootWeb;
            }
            else
            {
                site = siteCollection.AllWebs[siteName];
            }

            try
            {
                contentTypeExists = site.ContentTypes[contentTypeName] != null;
            }
            catch
            {
                contentTypeExists = false;
            }

            site.Dispose();
            return contentTypeExists;
        }

        public bool VerifyListExists(string siteName, string listName)
        {
            bool listExists = true;

            SPSite siteCollection = SPControl.GetContextSite(HttpContext.Current);

            SPWeb site = null;
            if (String.IsNullOrEmpty(siteName))
            {
                site = siteCollection.RootWeb;
            }
            else
            {
                site = siteCollection.AllWebs[siteName];
            }

            try
            {
                listExists = site.Lists[listName] != null;
            }
            catch
            {
                listExists = false;
            }

            site.Dispose();
            return listExists;
        }
    }
}
